/*
 * Copyright (c) 2022. China Mobile (SuZhou) Software Technology Co.,Ltd. All rights reserved.
 * Lakehouse is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package com.chinamobile.cmss.lakehouse.common.utils;

import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;

@Slf4j
public class BeanUtils {

    public static <T> T copyFrom(Object source, Class<T> classofT) {
        return copyFrom(source, classofT, null);
    }

    public static <T> T copyFrom(Object source, Class<T> innerClass, Class outerClass) {
        if (source == null) {
            return null;
        }
        try {
            T dest;
            if (outerClass == null) {
                dest = innerClass.newInstance();
            } else {
                dest = innerClass.getConstructor(outerClass).newInstance(outerClass.newInstance());
            }
            org.springframework.beans.BeanUtils.copyProperties(source, dest);
            return dest;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    public static <T> List<T> copyListFrom(List sourceList, Class<T> classofT) {
        return copyListFrom(sourceList, classofT, null);
    }

    public static <T> List<T> copyListFrom(List sourceList, Class<T> innerClass, Class outerClass) {
        List<T> list = new ArrayList<>();

        if (sourceList == null || sourceList.size() == 0) {
            return list;
        }

        sourceList.forEach(obj -> {
            T res;
            if (outerClass == null) {
                res = copyFrom(obj, innerClass);
            } else {
                res = copyFrom(obj, innerClass, outerClass);
            }

            if (res != null) {
                list.add(res);
            }
        });

        return list;
    }

    public static void copyProperties(Object source, Object target) throws BeansException {
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor targetPd : targetPds) {
            if (targetPd.getWriteMethod() != null) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method readMethod = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        // Check if value is null
                        if (value != null) {
                            Method writeMethod = targetPd.getWriteMethod();
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            writeMethod.invoke(target, value);
                        }
                    } catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }
    }
}
